﻿#ifndef BAIDUSPEECHSYNTHESIZER_H
#define BAIDUSPEECHSYNTHESIZER_H

#include <head/g_functionbase.h>
#include <head/m_head.h>

#define TOKEN_FILE "../etc/token.ini"
#define TOKEN_URL "http://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id=%1&client_secret=%2"

#define TTS_BASE_URL "http://tsn.baidu.com/text2audio"
#define TTS_URL "http://tsn.baidu.com/text2audio?tex=%1&lan=zh&cuid=%2&ctp=1&tok=%3"
#define TTS2_URL "http://tsn.baidu.com/text2audio?tex=%1&lan=zh&cuid=%2&ctp=1&tok=%3&per=4"
//tex 要合成的文本
//tok access_token
//cuid 用户唯一标识，机器 MAC 地址或 IMEI 码，长度为60字符以内
//lan 固定值zh。语言选择,目前只有中英文混合模式，填写固定值zh
//per 	选填,发音人选择, 0为女声，1为男声，3为情感合成-度逍遥，4为情感合成-度丫丫，默认为普通女声

#define VOP_URL "http://vop.baidu.com/server_api"

//Access Token 有效期为一个月
#define SESSION_FILE "/session.dat"

#define CACHE_LOCATION "../var/cache/speech"
#define CACHE_INDEX "../var/cache/speech/index"

//百度语音合成器，只负责获取mp3文件，不负责播放
class BaiduSpeechSynthesizer : public QObject
{
    Q_OBJECT
public:
    explicit BaiduSpeechSynthesizer(QObject *parent = nullptr)
        : QObject(parent)
    {
        macAddress=sp_getHarewareAddress();

        QDir cacheDir(CACHE_LOCATION);
        cacheLocation=cacheDir.absolutePath();
        if(!cacheDir.exists())
            cacheDir.mkpath(cacheLocation);

        QDir configDir(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
        QString configLocation=configDir.absolutePath();
        if(!configDir.exists())
            configDir.mkpath(configLocation);

        sessionFile=configLocation+ SESSION_FILE;

        manager=new QNetworkAccessManager(this);
        connect(manager,&QNetworkAccessManager::finished,
                this,&BaiduSpeechSynthesizer::onReplyFinished);

        detectToken();
    }

    void onReplyFinished(QNetworkReply *reply)
    {
        QString contentType=reply->header(QNetworkRequest::ContentTypeHeader).toString();
        if(contentType == "audio/mp3")
        {
            QString postfix=QUuid::createUuid().toString() + ".mp3";
            QString fileName=cacheLocation + "/" + postfix;
            QFile file(fileName);
            if(file.open(QFile::WriteOnly))
            {
                file.write(reply->readAll());
                file.close();
                emit finished(statement,fileName);
            }
        }
        else if(contentType == "application/json")
        {
            QJsonDocument doc=QJsonDocument::fromJson(reply->readAll());
            QJsonObject obj=doc.object();
            if(obj.contains("err_msg"))
            {
                QString msg=obj.value("err_msg").toString();
                emit errorMsg(msg);
            }
            else if(obj.contains("access_token"))
            {
                access_token=obj.value("access_token").toString();
                if(!access_token.isEmpty())
                    saveSession();
            }
        }
        reply->deleteLater();
    }

    void detectToken()
    {
        if(!QFile::exists(sessionFile))
            requestToken();
        else
            if(!loadSession())
                requestToken();
    }

    void requestStatement(const QString &request)
    {
        if(access_token.isEmpty())
            return;

        statement=request;
        QString url=QString(TTS2_URL)
                .arg(statement)
                .arg(macAddress)
                .arg(access_token);
        httpGet(url);
    }

    void requestToken(const QString &tokenFile=TOKEN_FILE)
    {
        if(!QFile::exists(tokenFile))
        {
            qDebug() << "Token File Not Found!!!";
            return;
        }

        QSettings set(tokenFile,QSettings::IniFormat);
        QString API_Key=set.value("Token/API_Key").toString();
        QString Secret_Key=set.value("Token/Secret_Key").toString();
        QString url=QString(TOKEN_URL).arg(API_Key).arg(Secret_Key);

        httpGet(url);
    }

    bool loadSession()
    {
        QFile file(sessionFile);
        if(file.open(QFile::ReadOnly))
        {
            QJsonDocument doc=QJsonDocument::fromBinaryData(file.readAll());
            QJsonObject obj=doc.object();
            if(obj.contains("date"))
            {
                QString dateStr=obj.value("date").toString();
                QDate date=QDate::fromString(dateStr,DATE_FORMAT);
                if(date.daysTo(QDate::currentDate()) > 28)
                    return false;

                access_token=obj.value("token").toString();
                return !access_token.isEmpty();
            }
            else
                return false;
        }
        return false;
    }

    bool saveSession()
    {
        if(access_token.isEmpty())
            return false;

        QJsonObject obj;
        obj.insert("date",Today);
        obj.insert("token",access_token);

        QFile file(sessionFile);
        if(file.open(QFile::WriteOnly))
        {
            QJsonDocument doc(obj);
            file.write(doc.toBinaryData());
            file.close();
            return true;
        }
        else
            return false;
    }

signals:
    void errorMsg(const QString &msg);
    void finished(const QString &text,const QString &fileName);

private:
    void httpGet(const QString &url)
    {
        manager->get(QNetworkRequest(QUrl(url)));
    }

    QNetworkAccessManager *manager;

    QString macAddress;
    QString statement;
    QString cacheLocation;
    QString access_token;
    QString sessionFile;
};

#endif // BAIDUSPEECHSYNTHESIZER_H
